

#include "grid3d.h"
#include "Mesh_grid3d.h"

#include <assert.h>
#include <stdlib.h>

#include <MeshInterface.hpp>
#include <MsqError.hpp>
#include <MsqVertex.hpp>
#include <Vector3D.hpp>
#include <MeshWriter.hpp>

static inline void validate_my_grid(grid3d_t *my_grid, Mesquite::MsqError &err)
{
    if (!my_grid || my_grid->NEL == 0)
    {
        MSQ_SETERR(err)("Invalid mesh", Mesquite::MsqError::INVALID_MESH);
        return;
    }
}

Mesh_grid3d::~Mesh_grid3d()
{
    delete myTags;
    if (vertex_bytes)
        free(vertex_bytes);
    if (own_my_grid)
        ReleaseGrid(&my_grid);
}

void Mesh_grid3d::read_g3t(const char *file_name, Mesquite::MsqError& err)
{
    if (my_grid)
        ReleaseGrid(&my_grid);
    my_grid = LoadGrid(file_name);
    if (!my_grid)
        MSQ_SETERR(err)("Cannot load grid from file", Mesquite::MsqError::IO_ERROR);
    own_my_grid = true;
}

void Mesh_grid3d::write_g3t(const char *file_name, Mesquite::MsqError& err)
{
    SaveGrid(my_grid, file_name);
}

void Mesh_grid3d::read_g3b(const char *file_name, Mesquite::MsqError& err)
{
    if (my_grid)
        ReleaseGrid(&my_grid);
    my_grid = (grid3d_t*) calloc(1, sizeof (grid3d_t));
    if (!my_grid)
        MSQ_SETERR(err)("Cannot allocate grid", Mesquite::MsqError::OUT_OF_MEMORY);
    FILE *F = fopen(file_name, "rb");
    if (F == NULL)
        MSQ_SETERR(err)("Cannot open file", Mesquite::MsqError::FILE_ACCESS);
    load_grid_bin(my_grid, F);
    UpdateMeasures(my_grid);
    MakeGridNameFromPath(my_grid, file_name);
    fclose(F);
    own_my_grid = true;
}

void Mesh_grid3d::write_g3b(const char *file_name, Mesquite::MsqError& err)
{
    FILE *F = fopen(file_name, "wb");
    if (F == NULL)
        MSQ_SETERR(err)("Cannot open file", Mesquite::MsqError::FILE_ACCESS);
    save_grid_bin(my_grid, F);
    fclose(F);
}

void Mesh_grid3d::get_all_elements(msq_std::vector<ElementHandle>& elements,
                                   Mesquite::MsqError& err)
{
    validate_my_grid(my_grid, err);
    MSQ_ERRRTN(err);
    elements.clear();
    elements.resize(my_grid->NEL);
    for (int i = 0; i < my_grid->NEL; i++)
        elements[i] = elem2handle(i);
}

void Mesh_grid3d::get_all_vertices(msq_std::vector<VertexHandle>& vertices,
                                   Mesquite::MsqError& err)
{
    validate_my_grid(my_grid, err);
    MSQ_ERRRTN(err);
    vertices.clear();
    vertices.resize(my_grid->NN);
    for (int i = 0; i < my_grid->NN; i++)
        vertices[i] = node2handle(i);
}

void Mesh_grid3d::vertices_get_fixed_flag(const VertexHandle vert_array[],
                                          bool fixed_flag_array[], size_t num_vtx, Mesquite::MsqError &err)
{
    validate_my_grid(my_grid, err);
    MSQ_ERRRTN(err);
    for (size_t i = 0; i < num_vtx; i++)
    {
        int n = handle2node(vert_array[i]);
        if (n >= my_grid->NN)
        {
            MSQ_SETERR(err)("Invalid vertex handle", Mesquite::MsqError::INVALID_ARG);
            return;
        }
        node_t *node = my_grid->nodes + n;
        bool fixed = false;
        if (fixed_DIR && node->bcond == BC_DIR)
            fixed = true;
        if (fixed_onbdry && node->on_bdry > 0)
            fixed = true;
        fixed_flag_array[i] = fixed;
    }

    return;
}

void Mesh_grid3d::vertices_get_coordinates(const VertexHandle vert_array[],
                                           Mesquite::MsqVertex* coordinates,
                                           size_t num_vtx,
                                           Mesquite::MsqError &err)
{
    validate_my_grid(my_grid, err);
    MSQ_ERRRTN(err);
    for (size_t i = 0; i < num_vtx; i++)
    {
        int n = handle2node(vert_array[i]);
        if (n >= my_grid->NN)
        {
            MSQ_SETERR(err)("Invalid vertex handle", Mesquite::MsqError::INVALID_ARG);
            return;
        }
        node_t *node = my_grid->nodes + n;
        coordinates[i].set(node->coords);
    }

    return;

}

void Mesh_grid3d::vertex_set_coordinates(VertexHandle vertex,
                                         const Mesquite::Vector3D &coordinates,
                                         Mesquite::MsqError &err)
{
    validate_my_grid(my_grid, err);
    MSQ_ERRRTN(err);
    int n = handle2node(vertex);
    if (n >= my_grid->NN)
    {
        MSQ_SETERR(err)("Invalid vertex handle", Mesquite::MsqError::INVALID_ARG);
        return;
    }
    node_t *node = my_grid->nodes + n;
    coordinates.get_coordinates(node->coords);
    return;
}

void Mesh_grid3d::calloc_vertex_bytes(Mesquite::MsqError &err)
{
    validate_my_grid(my_grid, err);
    MSQ_ERRRTN(err);
    if (vertex_bytes == NULL)
        vertex_bytes = (unsigned char *) calloc(my_grid->NN, sizeof (unsigned char));
    if (vertex_bytes == NULL)
        MSQ_SETERR(err)("Invalid vertex handle", Mesquite::MsqError::OUT_OF_MEMORY);
}

void Mesh_grid3d::vertex_set_byte(VertexHandle vertex,
                                  unsigned char byte,
                                  Mesquite::MsqError &err)
{
    calloc_vertex_bytes(err);
    MSQ_ERRRTN(err);
    int n = handle2node(vertex);
    if (n >= my_grid->NN)
    {
        MSQ_SETERR(err)("Invalid vertex handle", Mesquite::MsqError::INVALID_ARG);
        return;
    }
    vertex_bytes[n] = byte;
}

void Mesh_grid3d::vertices_set_byte(const VertexHandle *vert_array,
                                    const unsigned char *byte_array,
                                    size_t array_size,
                                    Mesquite::MsqError &err)
{
    calloc_vertex_bytes(err);
    MSQ_ERRRTN(err);
    for (size_t idx = 0; idx < array_size; idx++)
    {
        int n = handle2node(vert_array[idx]);
        if (n >= my_grid->NN)
        {
            MSQ_SETERR(err)("Invalid vertex handle", Mesquite::MsqError::INVALID_ARG);
            return;
        }
        vertex_bytes[n] = byte_array[idx];
    }
}

void Mesh_grid3d::vertex_get_byte(const VertexHandle vertex,
                                  unsigned char *byte,
                                  Mesquite::MsqError &err)
{
    validate_my_grid(my_grid, err);
    MSQ_ERRRTN(err);
    int n = handle2node(vertex);
    if (n >= my_grid->NN)
    {
        MSQ_SETERR(err)("Invalid vertex handle", Mesquite::MsqError::INVALID_ARG);
        return;
    }
    if (vertex_bytes)
        *byte = vertex_bytes[n];
    else
        *byte = 0;
}

void Mesh_grid3d::vertices_get_byte(const VertexHandle *vert_array,
                                    unsigned char *byte_array,
                                    size_t array_size,
                                    Mesquite::MsqError &err)
{
    validate_my_grid(my_grid, err);
    MSQ_ERRRTN(err);
    if (vertex_bytes)
        for (size_t idx = 0; idx < array_size; idx++)
        {
            int n = handle2node(vert_array[idx]);
            if (n >= my_grid->NN)
            {
                MSQ_SETERR(err)("Invalid vertex handle", Mesquite::MsqError::INVALID_ARG);
                return;
            }
            byte_array[idx] = vertex_bytes[n];
        }
    else
        memset(byte_array, 0, sizeof (unsigned char) * array_size);
}

void Mesh_grid3d::vertices_get_attached_elements(
                                                 const VertexHandle* vert_array, size_t num_vertex,
                                                 msq_std::vector<ElementHandle>& elements,
                                                 msq_std::vector<size_t>& offsets,
                                                 Mesquite::MsqError& err)
{
    validate_my_grid(my_grid, err);
    MSQ_ERRRTN(err);
    if (my_grid->inverse == NULL)
        MakeInverse(my_grid);
    elements.clear();
    offsets.clear();
    offsets.resize(num_vertex + 1);
    size_t current_offset = 0;
    for (size_t idx = 0; idx < num_vertex; idx++)
    {
        int n = handle2node(vert_array[idx]);
        if (n >= my_grid->NN)
        {
            MSQ_SETERR(err)("Invalid vertex handle", Mesquite::MsqError::INVALID_ARG);
            return;
        }
        offsets[idx] = current_offset;
        node_inv_t *ninv = my_grid->inverse->ninv + n;
        current_offset += ninv->NEL;
    }
    offsets[num_vertex] = current_offset;
    elements.resize(current_offset);
    current_offset = 0;
    for (size_t idx = 0; idx < num_vertex; idx++)
    {
        int n = handle2node(vert_array[idx]);
        node_inv_t *ninv = my_grid->inverse->ninv + n;
        for (int el_idx = 0; el_idx < ninv->NEL; el_idx++, current_offset++)
            elements[current_offset] = elem2handle(ninv->Tids[el_idx]);
    }
    return;
}

void Mesh_grid3d::elements_get_attached_vertices(
                                                 const ElementHandle *elem_handles, size_t num_elems,
                                                 msq_std::vector<VertexHandle>& vert_handles,
                                                 msq_std::vector<size_t>& offsets,
                                                 Mesquite::MsqError &err)
{
    validate_my_grid(my_grid, err);
    MSQ_ERRRTN(err);
    vert_handles.clear();
    vert_handles.resize(4 * num_elems);
    offsets.clear();
    offsets.resize(num_elems + 1);
    for (size_t idx = 0; idx < num_elems; idx++)
    {
        int el = handle2elem(elem_handles[idx]);
        if (el >= my_grid->NEL)
        {
            MSQ_SETERR(err)("Invalid element handle", Mesquite::MsqError::INVALID_ARG);
            return;
        }
        elem_t *T = my_grid->elems + el;
        for (unsigned i = 0; i < 4; i++)
            vert_handles[4 * idx + i] = node2handle(T->nodes[i]);
        offsets[idx] = 4 * idx;
    }
    offsets[num_elems] = 4 * num_elems;
}

void Mesh_grid3d::elements_get_topologies(
                                          const ElementHandle *element_handle_array,
                                          Mesquite::EntityTopology *element_topologies,
                                          size_t num_elements, Mesquite::MsqError &err)
{
    (void) element_handle_array;
    for (size_t idx = 0; idx < num_elements; idx++)
        element_topologies[idx] = Mesquite::TETRAHEDRON;
    return;
}

void Mesh_grid3d::release_entity_handles(const EntityHandle *handle_array,
                                         size_t num_handles, Mesquite::MsqError &err)
{
    /* do nothing */
}

void Mesh_grid3d::release(void)
{
    if (vertex_bytes)
        free(vertex_bytes);
    vertex_bytes = 0;
}

/**************************************************************************
 *                               TAGS
 **************************************************************************/

Mesquite::TagHandle Mesh_grid3d::tag_create(const msq_std::string& name,
                                            TagType type,
                                            unsigned length,
                                            const void* defval,
                                            Mesquite::MsqError& err)
{
    size_t index = myTags->create(name, type, length, defval, err);
    MSQ_ERRZERO(err);
    return (Mesquite::TagHandle)index;
}

void Mesh_grid3d::tag_delete(Mesquite::TagHandle handle, Mesquite::MsqError& err)
{
    myTags->destroy((size_t) handle, err);
    MSQ_CHKERR(err);
}

Mesquite::TagHandle Mesh_grid3d::tag_get(const msq_std::string& name, Mesquite::MsqError& err)
{
    size_t index = myTags->handle(name, err);
    MSQ_ERRZERO(err);
    return (Mesquite::TagHandle)index;
}

void Mesh_grid3d::tag_properties(Mesquite::TagHandle handle,
                                 msq_std::string& name,
                                 Mesquite::Mesh::TagType& type,
                                 unsigned& length,
                                 Mesquite::MsqError& err)
{
    const Mesquite::TagDescription& desc
            = myTags->properties((size_t) handle, err);
    MSQ_ERRRTN(err);

    name = desc.name;
    type = desc.type;
    length = (unsigned) (desc.size / Mesquite::MeshImplTags::size_from_tag_type(desc.type));
}

void Mesh_grid3d::tag_set_element_data(Mesquite::TagHandle handle,
                                       size_t num_elems,
                                       const ElementHandle* elem_array,
                                       const void* values,
                                       Mesquite::MsqError& err)
{
    myTags->set_element_data((size_t) handle,
                             num_elems,
                             (const size_t*) elem_array,
                             values,
                             err);
    MSQ_CHKERR(err);
}

void Mesh_grid3d::tag_get_element_data(Mesquite::TagHandle handle,
                                       size_t num_elems,
                                       const ElementHandle* elem_array,
                                       void* values,
                                       Mesquite::MsqError& err)
{
    myTags->get_element_data((size_t) handle,
                             num_elems,
                             (const size_t*) elem_array,
                             values,
                             err);
    MSQ_CHKERR(err);
}

void Mesh_grid3d::tag_set_vertex_data(Mesquite::TagHandle handle,
                                      size_t num_elems,
                                      const VertexHandle* elem_array,
                                      const void* values,
                                      Mesquite::MsqError& err)
{
    myTags->set_vertex_data((size_t) handle,
                            num_elems,
                            (const size_t*) elem_array,
                            values,
                            err);
    MSQ_CHKERR(err);
}

void Mesh_grid3d::tag_get_vertex_data(Mesquite::TagHandle handle,
                                      size_t num_elems,
                                      const VertexHandle* elem_array,
                                      void* values,
                                      Mesquite::MsqError& err)
{
    myTags->get_vertex_data((size_t) handle,
                            num_elems,
                            (const size_t*) elem_array,
                            values,
                            err);
    MSQ_CHKERR(err);
}








